package model.elements.ArtificialIntelligence;

import java.util.HashSet;
import java.util.Random;

import model.collisions.Collisionable;
import model.elements.NonPlayerCharacter;
import model.elements.PlayerCharacter;
import model.elements.attributes.Coordinate2D;
import model.elements.attributes.Speed;
import model.elements.commands.ElementCommand;
import model.elements.commands.ElementCommandFactory;
import model.elements.states.movement.ElementMovementState;
import model.elements.states.movement.MovingDown;
import model.elements.states.movement.MovingLeft;
import model.elements.states.movement.MovingRight;
import model.elements.states.movement.MovingUp;
import model.exceptions.CommandFactoryIsNotReady;
import model.exceptions.ModelExceptions;
import model.map.ElementsMap;
import model.movement.MovementEngine;

// TODO: Auto-generated Javadoc
/**
 * The Class MovesRandomUsesBullet.
 */
public class Tank implements ArtificialIntelligence
{	
	
	/** The m_state. */
	int m_moveState = 0;
	
	/** The m_used weapon. */
	boolean m_usedWeapon = false;
	
	/** The m_weapon state. */
	int m_weaponState = 0;
	
	/** The m_last direction. */
	ElementMovementState m_lastDirection;
	
	/** The m_coin flipper. */
	Random m_coinFlipper = new Random(); 
	
	/** The total move turns. */
	int totalMoveTurns;
	
	/** The m_map. */
	ElementsMap m_map;
	
	/**
	 * Instantiates a new moves random uses bullet.
	 *
	 * @param i the i
	 * @param map the map
	 */
	public Tank(int i, ElementsMap map)
	{
		m_lastDirection = getRandomDirection();
		totalMoveTurns = i;
		m_map = map;
	}
	
	/**
	 * Gets the random direction.
	 *
	 * @return the random direction
	 */
	private ElementMovementState getRandomDirection()
	{
		
		switch (m_coinFlipper.nextInt(4))
		{
			case 0:
				return new MovingRight();
			case 1:
				return new MovingLeft();
			case 2:
				return new MovingUp();
			case 3:
				return new MovingDown();
		}
		return m_lastDirection;		
	}
	
	/**
	 * Movement completed.
	 *
	 * @return true, if successful
	 */
	private boolean movementCompleted()
	{
		return m_moveState >= totalMoveTurns;
	}
	
	/**
	 * Reset movement state.
	 */
	private void resetMovementState()
	{
		m_moveState = 0;
	}
	
	/**
	 * Increase movement state.
	 */
	private void increaseMovementState()
	{
		++m_moveState;
	}
	
	/**
	 * Sets the weapon was used.
	 */
	private void setWeaponWasUsed()
	{
		m_usedWeapon = true;
		m_weaponState = 0;
	}
	
	/**
	 * Sets the weapon ca be used.
	 */
	private void setWeaponCaBeUsed()
	{
		m_usedWeapon = false;
		m_weaponState = 0;
	}
	
	/**
	 * Weapon has been used.
	 *
	 * @return true, if successful
	 */
	private boolean weaponHasBeenUsed()
	{
		return m_usedWeapon;
	}
	
	/**
	 * Will use weapon.
	 *
	 * @param character the character
	 * @return true, if successful
	 */
	private boolean willUseWeapon(NonPlayerCharacter character)
	{
		
	    // Mini patch para poder instanciar la IA tank sin el mapa, dsp ver de hacer una factory de ias o algo, para evitar tener q pasarle el mapa al npc para q este construya la ia
	    if ( m_map!=null )
	    {
	        HashSet<Collisionable> list = (HashSet <Collisionable>) m_map.getElementsIn( getShootTile(character) );
	        
	        for( Collisionable element : list )
	            if ( !element.getClass().equals( PlayerCharacter.class ) )return false;
	    }
		
		return m_coinFlipper.nextInt(10) == 0;
	}
	
	/**
	 * Weapon state completed.
	 *
	 * @return true, if successful
	 */
	private boolean weaponStateCompleted()
	{
		return m_weaponState >= 1;
	}
	
	/**
	 * Increase weapon state.
	 */
	private void increaseWeaponState()
	{
		m_weaponState++;
		if(weaponStateCompleted())
			setWeaponCaBeUsed();			
	}
	
	/**
	 * Sets the next move.
	 *
	 * @param direction the new next move
	 */
	private void setNextMove(ElementMovementState direction)
	{
		m_lastDirection = direction;
	}
	
	/**
	 * Gets the next move.
	 *
	 * @return the next move
	 */
	private ElementMovementState getNextMove()
	{
		return m_lastDirection;
	}
	
	/**
	 * Gets the shoot tile.
	 *
	 * @param character the character
	 * @return the shoot tile
	 */
	private Coordinate2D getShootTile(NonPlayerCharacter character)
	{
	    Coordinate2D shootTile = MovementEngine.getNextPositionForNonAcceleratedMovement
        (
			character.getPosition(), 
			character.getDirection(), 
			new Speed(1.0)
		);
		
		return shootTile;
	}
	
	/**
	 * Keep moving.
	 *
	 * @return the element command
	 * @throws CommandFactoryIsNotReady the command factory is not ready
	 */
	private ElementCommand keepMoving() throws CommandFactoryIsNotReady
	{
		increaseMovementState();
		return ElementCommandFactory.getElementMove( getNextMove() );
	}
	
	/**
	 * Use weapon.
	 *
	 * @param character the character
	 * @return the element command
	 * @throws CommandFactoryIsNotReady the command factory is not ready
	 */
	private ElementCommand useWeapon( NonPlayerCharacter character ) throws CommandFactoryIsNotReady
	{
		setWeaponWasUsed();
		setNextMove( character.getDirection().getOposingDirection() );
		return ElementCommandFactory.getElementUseWeapon();
	}
	
	/* (non-Javadoc)
	 * @see model.elements.ArtificialIntelligence.ArtificialIntelligence#getCommand(model.elements.NonPlayerCharacter)
	 */
	@Override
	public ElementCommand getCommand( NonPlayerCharacter character ) throws ModelExceptions
	{
		
		if ( !movementCompleted() )
			return keepMoving();
		
		resetMovementState();
		
		if( weaponHasBeenUsed() )
		{
			increaseWeaponState();
			return keepMoving();
		}				
		
		if( character.canUseWeapon() && willUseWeapon(character) ) 
			return  useWeapon( character );
		
		setNextMove( getRandomDirection() );
		
		return ElementCommandFactory.getElementMove( getNextMove() );
		
	}
	
}
